package com.lxd.filter.Impl;

import com.lxd.common.constants.CacheConstant;
import com.lxd.common.constants.SmsConstant;
import com.lxd.common.enums.ExceptionEnums;
import com.lxd.common.exception.StrategyException;
import com.lxd.common.model.StandardSubmit;
import com.lxd.feign.cacheClient;
import com.lxd.filter.CheckFilter;
import com.lxd.utils.ErrorSendMsgUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

/**
 * @Author Dongdong
 * @注释
 */
@Service("limitOneHour")
@Slf4j
public class LimitOneHourStrategyFilter implements CheckFilter {

    @Autowired
    private cacheClient client;
    @Autowired
    private ErrorSendMsgUtil errorSendMsgUtil;


    private final String UTC = "+8";

    private final long ONE_HOUR = 60 * 60 * 1000 - 1; //60s 的ms级别

    @Override
    public void check(StandardSubmit submit) {
        //如果不是验证码类型的短信，不会做限流
        if(submit.getState() != SmsConstant.CODE_TYPE){
            return;
        }
        //1、查询出短信发送时间
        LocalDateTime sendTime = submit.getSendTime();
        //转换为ms级别
        long sendTimeMilli = sendTime.toInstant(ZoneOffset.of(UTC)).toEpochMilli();
        submit.setOneHourLimitMilli(sendTimeMilli);

        //2、基于submit获取客户标识以及手机号信息 用来作为key
        Long clientId = submit.getClientId();
        String mobile = submit.getMobile();

        //3、优先将当前短信发送信息插入到Redis的ZSet结构中 zadd
        log.info("【策略模块-一小时限流】 正在插入redis");
        String key = CacheConstant.LIMIT_HOURS + clientId + CacheConstant.SEPARATE + mobile;

        //4.1、先插入redis
        Boolean is_ok = client.zadd(key, sendTimeMilli, sendTimeMilli);
        log.info("【策略模块-一小时限流】 插入结果为={}", is_ok);

         int retry=0;//重试次数
        while (!is_ok && retry<3){
            sendTimeMilli=sendTime.toInstant(ZoneOffset.of(UTC)).toEpochMilli();
            submit.setOneHourLimitMilli(sendTimeMilli);//更新发送时间
            is_ok = client.zadd(key, sendTimeMilli, sendTimeMilli);
            retry++;
        }
        if (retry==3){
            log.info("【策略模块-一小时限流】 已经达到最大重试次数3");
            submit.setErrorMsg(ExceptionEnums.ONE_HOUR_LIMIT + ",mobile = " + mobile);
            errorSendMsgUtil.sendWriteLog(submit);
            errorSendMsgUtil.sendPushReport(submit);
            throw new StrategyException(ExceptionEnums.ONE_HOUR_LIMIT);
        }

        //5、如果添加成功，再去查询过去60s内是否已经发送过超过1条
        long start = submit.getOneHourLimitMilli() - ONE_HOUR;//过去1h的开始 比如我插入数据时间是第5h，start=第4h

        //查询过去1h内发送短信数量
        int i = client.zRangeByScoreCount(key, Double.parseDouble(start + ""), Double.parseDouble(sendTimeMilli + ""));

        if (i > 3) {
            // 一分钟之前，发送过短信，限流规则生效
            log.info("【策略模块-一小时限流】  插入失败！ 满足一小时限流规则，无法发送！");
            client.zRemove(key,submit.getOneHourLimitMilli() + "");//删除掉插入的短信
            submit.setErrorMsg(ExceptionEnums.ONE_HOUR_LIMIT + ",mobile = " + mobile);
            errorSendMsgUtil.sendWriteLog(submit);
            errorSendMsgUtil.sendPushReport(submit);
            throw new StrategyException(ExceptionEnums.ONE_HOUR_LIMIT);
        }

        log.info("【策略模块-一小时限流】  一小时限流规则通过，可以发送！");
    }
}
